package whf.framework.web.struts;

import java.util.Enumeration;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.struts.action.ActionMapping;

import whf.framework.entity.Entity;
import whf.framework.exception.NotFoundException;
import whf.framework.exception.ServiceNotFoundException;
import whf.framework.log.Log;
import whf.framework.log.LogFactory;
import whf.framework.meta.MetaFactory;
import whf.framework.security.UserContext;
import whf.framework.security.entity.User;
import whf.framework.service.Service;
import whf.framework.util.BeanFactory;
import whf.framework.util.LongList;
import whf.framework.util.ParamUtils;
import whf.framework.util.StringUtils;
import whf.framework.util.Utils;
import whf.framework.web.Constants;
import whf.framework.web.PageMessages;
import whf.framework.web.WebContextHolder;

/**
 * 用于web封装数据
 * @author wanghaifeng
 * @create Nov 15, 2006 11:28:46 PM
 * 
 */
public class WebData {
	private static Log log = LogFactory.getLog(WebData.class);
	
	private UserContext userContext;
	
	private HttpServletRequest request;
	
	private HttpServletResponse response;
	
	private ActionMapping mapping;
	
	private String webMethod;
	
	private Map<String, Object> datas = Utils.newHashMap();
	
	private List<String> parameterNames ;
	
	private List<String> registeredParameterNames = Utils.newArrayList();
	
	/**
	 * 当前业务对象，在修改之前表示当前的baseForm，修改后则表示修改后的对象,用于将基类中的数据传出来，以便方便代码共享
	 */
	private Entity currentEntity;
	
	public WebData(HttpServletRequest req, HttpServletResponse res, ActionMapping mapping, String webMethod){
		this.request = req;
		this.response = res;
		this.mapping = mapping;
		this.webMethod = webMethod;
		//
		this.userContext = WebContextHolder.getWebContext().getUserContext();
		this.setAttribute(Constants.STRUTS_LASTEST_WEBMETHOD, webMethod);
	}

	/**
	 * @return Returns the request.
	 */
	public HttpServletRequest getRequest() {
		return request;
	}

	/**
	 * @return Returns the response.
	 */
	public HttpServletResponse getResponse() {
		return response;
	}
	
	public String getWebMethod(){
		return this.webMethod;
	}
	
	/**
	 * @modify wanghaifeng Nov 16, 2006 12:44:43 PM
	 * @return
	 */
	public ActionMapping getMapping(){
		return mapping;
	}
	
	/**
	 * @modify wanghaifeng Nov 16, 2006 12:45:32 PM
	 * @param forward
	 * @return
	 */
	public WebForward forward(String forward){
		return new WebForward(this, forward, "global-"+forward);
	}
	
	/**
	 * 如果当前对象没有对应的forward，系统将会使用一个缺省的，往往定义在global中
	 * @modify wanghaifeng Nov 16, 2006 12:48:04 PM
	 * @param forward
	 * @param defaultName
	 * @return
	 */
	public WebForward forward(String forward, String defaultName){
		return new WebForward(this, forward, defaultName);
	}

	/**
	 * @return Returns the userContext.
	 */
	public UserContext getUserContext() {
		return userContext;
	}
	
	public Object getData(String parameter){
		if(this.datas.containsKey(parameter)){
			Object value = this.datas.get(parameter);
			return value == null? null: value.toString();
		} else {
			Object val = this.request.getParameter(parameter);
			if(val == null) {
				val = this.request.getParameter(Constants.REQUEST_KEEP_PARAMETER_PREFFIX + parameter);
			}
			return val;
		}
	}
	
	public void putData(String key, Object data){
		this.registerParameterName(key);
		this.datas.put(key, data);
	}
	
	public String getParameter(String parameter){
		Object val = this.getData(parameter);
		if(val != null){
			return val.toString();
		} else {
			return null;
		}
	}
	
	public String[] getParameterValues(String parameter){
		return this.request.getParameterValues(parameter);
	}
	
	public boolean getBooleanParameter(String p){
		return "true".equalsIgnoreCase(getParameter(p)) || "on".equalsIgnoreCase(getParameter(p));
	}
	
	/**
	 * 参见getLongParameter(String[], long);
	 * @modify wanghaifeng Dec 27, 2006 10:57:55 AM
	 * @param ps
	 * @param def
	 * @return
	 */
	public int getIntParameter(String[] ps, int def){
		for(String p: ps){
			try{
				return Integer.parseInt(request.getParameter(p));
			}catch(Exception e){
				continue;
			}
		}
		return def;
	}
	
	public int getIntParameter(String p, int defaultValue){
		try{
			return Integer.parseInt(getParameter(p));
		} catch (Exception e){
			return defaultValue;
		}
	}
	
	public int[] getIntParameters(String p, int def){
		String[] vals = this.getParameterValues(p);
		if(vals == null || vals.length == 0) return null;
		int[] res = new int[vals.length];
		for(int i=0; i< vals.length; i++){
			String v  = vals[i];
			try{
				res[i] = Integer.parseInt(v);
			}catch(Exception e){
				res[i] = def;
			}
		}
		return res;
	}
	
	/**
	 * 对于需要使用替换的情况，使用如下方法获取
	 * 相当如：
	 * if p1 is long then
	 * 	return p1
	 * else if p2 is long then
	 *  return p2
	 * else return def;
	 * @modify wanghaifeng Dec 27, 2006 10:53:16 AM
	 * @param ps
	 * @param def
	 * @return
	 */
	public long getLongParameter(String[] ps, long def){
		for(String p: ps){
			try{
				return Long.parseLong(getParameter(p));
			}catch(Exception e){
				continue;
			}
		}
		return def;
	}
	
	/**
	 * 获取常整型
	 * @modify wanghaifeng Dec 27, 2006 10:54:53 AM
	 * @param p
	 * @param def
	 * @return
	 */
	public long getLongParameter(String p, long def){
		try{
			return Long.parseLong(getParameter(p));
		}catch(Exception e){
			return def;
		}
	}
	
	public long getLongAttribute(String p, long def){
		try{
			return Long.parseLong((this.request.getAttribute("p")).toString());
		} catch(Exception e){
			return def;
		}
	}
	
	/**
	 * 获取多个值
	 * @modify wanghaifeng Dec 27, 2006 10:58:22 AM
	 * @param p
	 * @param def
	 * @return
	 */
	public long[] getLongParameters(String p, long def){
		String[] vals = this.getParameterValues(p);
		if(vals == null || vals.length == 0) return null;
		long[] res = new long[vals.length];
		for(int i=0; i< vals.length; i++){
			String v  = vals[i];
			try{
				res[i] = Long.parseLong(v);
			}catch(Exception e){
				res[i] = def;
			}
		}
		return res;
	}
	
	/**
	 * 参见getLongParameter(String[], double);
	 * @modify wanghaifeng Dec 27, 2006 10:56:19 AM
	 * @param ps
	 * @param def
	 * @return
	 */
	public double getDoubleParameter(String[] ps, double def){
		for(String p: ps){
			try{
				return Double.parseDouble(request.getParameter(p));
			}catch(Exception e){
				continue;
			}
		}
		return def;
	}
	
	/**
	 * 获取double类型的值
	 * @modify wanghaifeng Dec 27, 2006 10:56:40 AM
	 * @param p
	 * @param def
	 * @return
	 */
	public double getDoubleParameter(String p, double def) {
		try{
			return Double.parseDouble(getParameter(p));
		}catch(Exception e){
			return def;
		}
	}
	
	public double[] getDoubleParameters(String p, double def){
		String[] vals = this.getParameterValues(p);
		if(vals == null || vals.length == 0) return null;
		double[] res = new double[vals.length];
		for(int i=0; i< vals.length; i++){
			String v  = vals[i];
			try{
				res[i] = Double.parseDouble(v);
			}catch(Exception e){
				res[i] = def;
			}
		}
		return res;
	}
	
	/**
	 * 获取tableTag中属性名称前缀
	 * 如属性前缀为pre,属性名称为name，tabletag中输出的属性为"prename"，主要用来区分同一个page中多个tabletag的同名属性的情况
	 * @param type
	 * @return
	 */
	public String getNamePrefix(Class type){
		String namePrefix =request.getParameter(type.getName()+Constants.NAME_PREFIX); //get the name prefix
		if(namePrefix == null) namePrefix = "";
		return namePrefix;
	}

	/**
	 * 是否使用主键作为属性名称后缀，对于tabletag中列表显示，如果要求修改的话，用来识别每一个对象；
	 * @param type
	 * @return
	 */
	public boolean isPkSuffix(Class type ){
		return ParamUtils.getBooleanParameter(request, type.getName()+Constants.PK_SUFFIX); //whether is pk suffix
	}
	

	/**
	 * 根据命名规则获取所有对象的id,适用于delete id获取等
	 * @param request
	 * @param type
	 * @param defaultValue
	 * @return
	 */
	public long[] getIds(Class type, long defaultValue){
		String namePrefix = getNamePrefix(type);
		boolean pkSuffix = isPkSuffix(type);
		if(!pkSuffix){
			return getLongParameters(namePrefix+Constants.ID,defaultValue);
		} else{
			LongList list = new LongList();
			List<String> paramNames = getParameterNames();
			for(String paramName: paramNames){
				if(paramName.startsWith(namePrefix+Constants.ID)){
					list.addAll(getLongParameters(paramName, defaultValue));
				}
			}
			return list.toArray();
		}
	}
	
	/**
	 * 获取一个普通属性的String值
	 * @param request
	 * @param type
	 * @param property
	 * @param pk
	 * @return
	 */
	public String getParameter(Class type, String property, long pk){
		String namePrefix = getNamePrefix(type);
		boolean pkSuffix = isPkSuffix(type);
		if(!pkSuffix){
			return getParameter(namePrefix+property);
		} else{
			return getParameter(namePrefix+property+pk);
		}
	}
	
	public String getParameter(Class type, String property){
		String namePrefix = getNamePrefix(type);
		boolean pkSuffix = isPkSuffix(type);
		if(!pkSuffix){
			String p = getParameter(namePrefix+property);
			if(StringUtils.isEmpty(p)){
				p = getParameter(namePrefix+property+Constants.BOID_SUFFIX); //适用于字符串绑定业务对象的情况，省去meta检查
			}
			return p;
		} else{
			long[] pks = getIds(type, 0);
			long pk = 0;
			if(pks != null && pks.length > 0){
				pk = pks[0];
			}
			String p = getParameter(namePrefix+property+pk);
			if(StringUtils.isEmpty(p)){
				p = getParameter(namePrefix+property+pk+Constants.BOID_SUFFIX);
			}
			return p;
		}
	}
	
	/**
	 * 获取多对一many-to-one属性的id，进而决定对象
	 * @param request
	 * @param propertyType 属性类型，当有前缀时可以用作获取前缀等功效
	 * @param propertyName 属性名称
	 * @param defaultValue 缺省值
	 * @param boId 当前正在操作（创建或修改）对象的id，当有后缀时boId是属性名称的后缀
	 * @return
	 */
	public long getManyToOnePropertyId(Class propertyType, String propertyName, long defaultValue, long boId){
		String namePrefix = getNamePrefix(propertyType);
		boolean pkSuffix = isPkSuffix(propertyType);
		if(!pkSuffix){
			return getLongParameter(namePrefix+propertyName+Constants.BOID_SUFFIX,defaultValue);
		} else{
			List<String> paramNames = getParameterNames();
			String parameterName = namePrefix+propertyType+boId+Constants.BOID_SUFFIX;
			for(String paramName: paramNames){
				if(paramName.equals(parameterName)){
					return getLongParameter(paramName, defaultValue);
				}
			}
			return defaultValue;
		}
	}
	
	/**
	 * 获取对象类型参数,使用非惰性加载方式获取
	 * @modify wanghaifeng Jan 9, 2007 11:10:32 PM
	 * @param propertyType
	 * @param propertyName
	 * @param defaultValue
	 * @param boId
	 * @return
	 */
	public Entity getBoParameterWithoutLazy(Class propertyType, String propertyName, long defaultValue, long boId){
		long key = getManyToOnePropertyId(propertyType, propertyName, defaultValue, boId);
		if(key <= 0){
			return null;
		} else {
			try{
				Service<? extends Entity> service = BeanFactory.getService(MetaFactory.findByModelClass(propertyType));
				Entity entity = service.findByPrimaryKeyWithoutLazy(new Long(key));
				return entity;
			}catch(ServiceNotFoundException e){
				log.warn(this, e);
			}catch(NotFoundException e){
				log.warn(this, e);
			}catch(Exception e){
				log.warn(this, e);
			}
			return null;
		}
	}
	
	/**
	 * 获取对象类型参数,使用缺省加载方式获取
	 * @modify wanghaifeng Jan 9, 2007 11:10:32 PM
	 * @param propertyType
	 * @param propertyName
	 * @param defaultValue
	 * @param boId
	 * @return
	 */
	public Entity getEntityParameter(Class propertyType, String propertyName, long defaultValue, long boId){
		long key = getManyToOnePropertyId(propertyType, propertyName, defaultValue, boId);
		if(key <= 0){
			return null;
		} else {
			try{
				Service service = BeanFactory.getService(MetaFactory.findByModelClass(propertyType));
				Entity entity = service.findByPrimaryKey(key);
				return entity;
			}catch(ServiceNotFoundException e){
				log.warn(this, e);
			}catch(NotFoundException e){
				log.warn(this, e);
			}catch(Exception e){
				log.warn(this, e);
			}
			return null;
		}
	}
	
	/**
	 * 对于Collection属性的多选或者单选时所有业务属性中对象的id；
	 * 获取时需要考虑tabletag的namePrefix和pksuffix属性
	 * @param request
	 * @param entity
	 * @param propertyName
	 * @param defaultValue
	 * @return
	 */
	public long[] getCollectionPropertyIds(Entity entity, String propertyName, long defaultValue){
		Class entityClass = entity.getClass();
		String namePrefix = getNamePrefix(entityClass);
		boolean pkSuffix = isPkSuffix(entityClass);
		String paramName = namePrefix+propertyName+Constants.BOID_SUFFIX;
		if(pkSuffix){
			paramName = namePrefix+propertyName+entity.getId()+Constants.BOID_SUFFIX;
		}
		String paramValue = getParameter(paramName);
		LongList list = new LongList();
		if(paramValue != null){
			String[] ss = StringUtils.split(paramValue, ",");
			for(int i=0;i<ss.length; i++){
				try{
					long newId = Long.parseLong(ss[i]);
					if(newId > 0){
						boolean found = false;
						for(long oldId: list.toArray()){
							if(oldId == newId){
								found = true;
								break;
							}
						}
						if(!found){
							list.add(newId);
						}
					}
				}catch(Exception e){
					log.warn(this, e);
				}
			}
		}
		return list.toArray();
	}
	
	/**
	 * 获取request中所有parameter名称
	 * @param request
	 * @return
	 */
	public List<String> getParameterNames(){
		if(this.parameterNames == null) {
			this.parameterNames = Utils.newArrayList();
			Enumeration enumParamNames = request.getParameterNames();
			while(enumParamNames.hasMoreElements()){
				parameterNames.add(enumParamNames.nextElement().toString());
			}
		}
		return parameterNames;
	}
	
	public void registerParameterName(String parameterName){
		if(StringUtils.isEmpty(parameterName)) return;
		this.registeredParameterNames.add(parameterName);
	}
	
	/**
	 * 是否包含参数
	 * @modify wanghaifeng Nov 16, 2006 12:42:38 PM
	 * @param param
	 * @return
	 */
	public boolean containsParameter(String param){
		return this.getParameterNames().contains(param) || 
		this.registeredParameterNames.contains(param);
	}
	
	public Object getAttribute(String attributeName){
		if(attributeName == null) 
			return request.getAttribute(mapping.getAttribute());
		
		Object res = request.getAttribute(attributeName);
		if(res == null){
			res = request.getSession().getAttribute(attributeName);
		}
		return res;
	}
	
	public void setAttribute(Object value){
		this.request.setAttribute(this.mapping.getAttribute(), value);
	}
	
	public void setAttribute(String key, Object value){
		this.setAttribute(key, value, Scope.REQUEST);
	}
	
	public void setAttribute(String key, Object value, Scope scope){
		if(StringUtils.isEmpty(key)) return;
		if(scope == Scope.HEADER){
			if(value != null)
				response.setHeader(key, value.toString());
		} else if(scope == Scope.SESSION){
			request.getSession().setAttribute(key, value);
		} else {
			request.setAttribute(key, value);
		}
	}
	
	public User getCurrentUser(){
		return this.userContext == null? null: this.userContext.getUser();
	}
	
	public HttpSession getSession(){
		return this.request.getSession();
	}
	
	/**
	 * 向页面中增加消息，增加的消息可以使用MessageTag查看
	 * @modify wanghaifeng Apr 9, 2007 12:51:33 PM
	 * @param msg
	 */
	public final void addPageMessage(Object msg){
		PageMessages msgs = (PageMessages)request.getAttribute(Constants.PAGE_MESSAGE_OBJECTS);
		if(msgs == null){
			msgs = new PageMessages();
			request.setAttribute(Constants.PAGE_MESSAGE_OBJECTS, msgs);
		}
		msgs.addMessage(msg);
	}

	/**
	 * @return Returns the currentBO.
	 */
	public Entity getCurrentEntity() {
		if(this.currentEntity == null)
			return (Entity)request.getAttribute(this.mapping.getAttribute());
		return currentEntity;
	}

	/**
	 * @param currentBO The currentBO to set.
	 */
	public void setCurrentEntity(Entity currentBO) {
		this.currentEntity = currentBO;
	}
	
	public enum Scope{
		/**
		 * 头
		 * @property Scope:APPLICATION
		 */
		HEADER,
		
		/**
		 * 回话
		 * @property Scope:SESSION
		 */
		SESSION,
		
		/**
		 * 请求
		 * @property Scope:REQUEST
		 */
		REQUEST
	}
}
